#include "stdafx.hpp"
#include "ColligereConfigHandler.hpp"
#include "ColligereApp.hpp"
#include "ColligereUtils.hpp"
#include "ColligereVersion.hpp"

////////////////////////////////////////////////
// ColligereConfigVariable
////////////////////////////////////////////////

col::ConfigVariable::ConfigVariable 
( wxUint32 id,  const wxString &rVariable, const wxString &rDescr, wxUint32 varType ) : 
mId ( id ), mVar ( rVariable ), mDescr ( rDescr ), mType ( varType )
{
}

bool col::ConfigVariable::operator == ( wxUint32 id ) const
{
	return id == mId;
}

bool col::ConfigVariable::operator < ( wxUint32 id ) const
{
	return id < mId;
}

const wxString &col::ConfigVariable::GetName() const 
{ 
	return mVar; 
}

const wxString &col::ConfigVariable::GetDescription() const 
{ 
	return mDescr; 
}

wxUint32 col::ConfigVariable::GetId() const 
{ 
	return mId; 
}

wxUint32 col::ConfigVariable::GetType() const 
{ 
	return mType; 
}

////////////////////////////////////////////////
// ColligereConfigHandler
////////////////////////////////////////////////

col::ConfigHandler::ConfigHandler() : mpCfg ( wxConfigBase::Get() )
{
	typedef ConfigVariable cv;

	// Initialize config variables.

	CreateUsrModifiableVar ( cv::SaveDatabaseOnClose, 
		"Database/SaveOnExit", _("Save database changes on close") );

	CreateUsrModifiableVar ( cv::DatabaseSyncType, "Database/SyncType" );

	CreateUsrModifiableVar ( cv::DatabaseCompressionLevel, 
		"Database/CompressionLevel" );

	CreateUsrModifiableVar ( cv::DefaultDatabase, "Database/Default" );

	CreateUsrModifiableVar ( cv::AutoVacuumDatabase, 
		"Database/AutoVacuum" );

	CreateUsrModifiableVar ( cv::AskBeforeApplyingValues,
		"General/AskBeforeApplyingValues", _("Ask before applying new values") );

	CreateUsrModifiableVar ( cv::AskBeforeExiting,
		"General/AskBeforeExiting", _("Ask before exiting") );

	CreateUsrModifiableVar ( cv::DumpLogToFileOnExit, 
		"General/DumpLogToFileOnExit", _("Save log to file on exit") );

	CreateUsrModifiableVar ( cv::ShowToolTips, 
		"General/ShowToolTips", _("Show tooltips") );

	CreateUsrModifiableVar ( cv::SaveLayoutOnExit, 
		"Layout/SaveOnExit", _("Save layout on exit") );

	CreateUsrModifiableVar ( cv::DisableAutoComplete, 
		"General/DisableAutoComplete", _("Disable auto-complete") );

	CreateUsrModifiableVar ( cv::Language, "General/Language" );

	CreateUsrModifiableVar ( cv::FreeDbUserEmail, 
		"FreeDb/UserEmail", _("Your E-Mail Address") );

	CreateUsrModifiableVar ( cv::FreeDbServerAddr, 
		"FreeDb/ServerAddr", _("Server Address") );

	CreateUsrModifiableVar ( cv::FreeDbAutoSetFormatToCd, 
		"FreeDb/AutoSetFormatToCd", _("Automatically Set Format To CD") );

	// Internal variables

	CreatePrivateVar ( cv::IsFirstAppExecution, "IsFirstTime" );

	CreatePrivateVar ( cv::CurrentColligereVersion,
		"CurrentColligereVersion" );

	CreatePrivateVar ( cv::SearchTerms, "Search/SearchTerms" );

	CreatePrivateVar ( cv::CaseSensitiveSearchChecked, 
		"Search/CaseSensitiveSearchChecked" );

	CreatePrivateVar ( cv::UseRegExSearchChecked, 
		"Search/UseRegExSearchChecked" );

	CreatePrivateVar ( cv::IsMainFrameMaximized,
		"Layout/IsMainFrameMaximized" );

	CreatePrivateVar ( cv::MainFramePosition, 
		"Layout/Positions/MainFrame" );

	CreatePrivateVar ( cv::MainFrameSize, 
		"Layout/Sizes/MainFrame" );

	CreatePrivateVar ( cv::MainFrameWxAuiSettings,
		"Layout/wxAUI/FrameLayout" );

	CreatePrivateVar ( cv::LogDialogSize,
		"Layout/Sizes/LogDialog" );

	CreatePrivateVar ( cv::LogDialogPosition,
		"Layout/Positions/LogDialog" );

	CreatePrivateVar ( cv::AudioDatabaseWxAuiSettings,
		"Layout/wxAUI/AudioDatabaseLayout" );

	CreatePrivateVar ( cv::LastOpenedDatabaseDir,
		"History/LastOpenedDatabaseDir" );

	CreatePrivateVar ( cv::LastSavedLogDir,
		"History/LastSavedLogDir" );

	const wxString prev_version ( GetValue ( cv::CurrentColligereVersion ) );
	double ver = 0;
	try
	{
		 ver = boost::lexical_cast < double > ( prev_version );
	}
	catch ( boost::bad_lexical_cast & ) { }

	if ( prev_version.empty() || ver < 0.5 )
	{
		// The main frame's AUI layout must be resetted or else the toolbar
		// will be rendered incorrectly (older versions used
		// wxToolBar while wxAuiToolbar has been used since 0.50).
		SetValue ( cv::MainFrameWxAuiSettings, empty_string );

		// Not completely nessessary but since the database layout has changed 
		// a lot, it's best to do this just in case.
		SetValue ( cv::AudioDatabaseWxAuiSettings, empty_string );
	}
	WriteDefaultsIfMissing();
}

col::ConfigHandler::~ConfigHandler()
{
	delete wxConfigBase::Set ( NULL );
}

void col::ConfigHandler::CreateUsrModifiableVar 
( wxUint32 id, const wxString &rName, const wxString &rDescr )
{
	mCfgVars.push_back ( ConfigVariable ( id, rName, rDescr,
		ConfigVariable::User ) );
}

void col::ConfigHandler::CreatePrivateVar ( wxUint32 id, const wxString &rName )
{
	mCfgVars.push_back ( ConfigVariable ( id, rName, empty_string, 
		ConfigVariable::Private ) );
}

bool col::ConfigHandler::GetVar ( wxUint32 varId, ConfigVariable &rTo ) const
{
	ConfigVariableDeque::const_iterator it = std::find ( mCfgVars.begin(), 
		mCfgVars.end(), varId );
	if ( it != mCfgVars.end() ) 
	{
		rTo = *it;
	}
	return it != mCfgVars.end();
}

bool col::ConfigHandler::Exists ( wxUint32 varId ) const
{
	return mpCfg->Exists ( ConvertVarIdToVarName ( varId ) );
}

bool col::ConfigHandler::IsEnabled ( const wxString &rVariable ) const
{
	long enabled = 0;
#ifdef __WXDEBUG__
	const bool get_ok = GetValue ( rVariable, enabled );
	wxASSERT ( get_ok );
	return get_ok ? enabled != 0 : false;
#else
	GetValue ( rVariable, enabled );
	return enabled != 0;
#endif
}

bool col::ConfigHandler::IsEnabled ( wxUint32 varId ) const
{
	return IsEnabled ( ConvertVarIdToVarName ( varId ) );
}

bool col::ConfigHandler::DeleteAll()
{
	return mpCfg->DeleteAll();
}

void col::ConfigHandler::SaveFileHistory ( wxFileHistory &rHistory )
{
	rHistory.Save ( *mpCfg );
}

void col::ConfigHandler::LoadFileHistory ( wxFileHistory &rHistory ) const
{
	rHistory.Load ( *mpCfg );
}

void col::ConfigHandler::LoadLastDirectory ( wxUint32 dirVarId, 
											wxFileDialog &rToDlg ) const
{
	const wxString dir_val ( GetValue ( dirVarId ) );
	if ( wxDirExists ( dir_val ) )
	{
		rToDlg.SetPath ( dir_val );
	}
}

void col::ConfigHandler::WriteDefaultsIfMissing()
{
	wxLogDebug ( "Writing missing default config values..." );
	typedef ConfigVariable cv;

	SetValueIfMissing ( cv::SaveDatabaseOnClose, false );
	SetValueIfMissing ( cv::AskBeforeApplyingValues, false );
	SetValueIfMissing ( cv::AskBeforeExiting, true );
	SetValueIfMissing ( cv::SaveLayoutOnExit, true );
	SetValueIfMissing ( cv::IsMainFrameMaximized, false );
	SetValueIfMissing ( cv::DumpLogToFileOnExit, false );
	SetValueIfMissing ( cv::ShowToolTips, true );
	SetValueIfMissing ( cv::AutoVacuumDatabase, false );
	SetValueIfMissing ( cv::DisableAutoComplete, false );

	SetValueIfMissing ( cv::DatabaseSyncType, "OFF" );
	SetValueIfMissing ( cv::DatabaseCompressionLevel, 9 );

	SetValueIfMissing ( cv::CaseSensitiveSearchChecked, false );
	SetValueIfMissing ( cv::UseRegExSearchChecked, false );

	SetValueIfMissing ( cv::FreeDbAutoSetFormatToCd, true );
	SetValueIfMissing ( cv::FreeDbServerAddr, "http://freedb.freedb.org" );
	SetValueIfMissing ( cv::FreeDbUserEmail, "your@email.com" );

	SetValueIfMissing ( cv::CurrentColligereVersion, col::VERSION_NUMBER );
}

void col::ConfigHandler::WriteDefaults ( bool forceWrite )
{
	if ( !mpCfg->Exists ( "IsFirstTime" ) || forceWrite )
	{
		wxLogDebug ( "Writing default config values..." );

		typedef ConfigVariable cv;

		mpCfg->DeleteAll();

		SetValue ( cv::SaveDatabaseOnClose, false );
		SetValue ( cv::AskBeforeApplyingValues, false );
		SetValue ( cv::AskBeforeExiting, true );
		SetValue ( cv::SaveLayoutOnExit, true );
		SetValue ( cv::IsMainFrameMaximized, false );
		SetValue ( cv::DumpLogToFileOnExit, false );
		SetValue ( cv::ShowToolTips, true );
		SetValue ( cv::AutoVacuumDatabase, false );
		SetValue ( cv::DisableAutoComplete, false );

		SetValue ( cv::DatabaseSyncType, "OFF" );
		SetValue ( cv::DatabaseCompressionLevel, 9 );
	
		SetValue ( cv::CaseSensitiveSearchChecked, false );
		SetValue ( cv::UseRegExSearchChecked, false );

		SetValue ( cv::FreeDbAutoSetFormatToCd, true );
		SetValue ( cv::FreeDbServerAddr, "http://freedb.freedb.org" );
		SetValue ( cv::FreeDbUserEmail, "your@email.com" );

		SetValue ( cv::IsFirstAppExecution, false );
	}
}

const wxPoint col::ConfigHandler::GetPosition ( const wxString &rVariable ) const
{
	const wxString val ( GetValue ( rVariable ) );

	if ( !val.empty() )
	{
		try
		{
			const wxUint32 x = boost::lexical_cast < wxUint32 > 
				( val.BeforeFirst (',') );
			const wxUint32 y = boost::lexical_cast < wxUint32 > 
				( val.AfterFirst ( ',') );

			const wxSize scr = wxGetDisplaySize();
		
			return wxPoint ( std::min < wxInt32 > ( x, scr.x ), 
				std::min < wxInt32 > ( y, scr.y ) );
		}
		catch ( boost::bad_lexical_cast &rE )
		{
			wxFAIL_MSG ( rE.what() );
		}
	}
	return wxDefaultPosition;
}

const wxPoint col::ConfigHandler::GetPosition ( wxUint32 varId ) const
{
	return GetPosition ( ConvertVarIdToVarName ( varId ) );
}

bool col::ConfigHandler::SetPosition ( const wxString &rVariable,
									  const wxPoint &rPos )
{
	return SetValue ( rVariable, boost::str ( boost::format 
		( "%1%,%2%" ) % rPos.x % rPos.y ) );
}

bool col::ConfigHandler::SetPosition ( wxUint32 varId, const wxPoint &rPos )
{
	return SetValue ( varId, boost::str ( boost::format 
		( "%1%,%2%" ) % rPos.x % rPos.y ) );
}

const wxSize col::ConfigHandler::GetSize ( const wxString &rVariable ) const
{
	using namespace boost;

	try
	{
		const wxString val ( GetValue ( rVariable ) );

		if ( !val.empty() )
		{
			const wxUint32 width = lexical_cast < wxUint32 > 
				( val.BeforeFirst ( ',') );
			const wxUint32 height = lexical_cast < wxUint32 > 
				( val.AfterFirst ( ',') );
			
			const wxSize scr = wxGetDisplaySize();
		
			return wxSize ( std::min < wxInt32 > ( width, scr.x ), 
				std::min < wxInt32 > ( height, scr.y ) );
		}
	}
	catch ( bad_lexical_cast &rE )
	{
		wxFAIL_MSG ( rE.what() );
	}
	return wxDefaultSize;
}

const wxSize col::ConfigHandler::GetSize ( wxUint32 varId ) const
{
	return GetSize ( ConvertVarIdToVarName ( varId ) );
}

bool col::ConfigHandler::SetSize ( const wxString &rVariable, const wxSize &rSize )
{
	return SetValue ( rVariable, boost::str ( boost::format 
		( "%1%,%2%" ) % rSize.GetWidth() % rSize.GetHeight() ) );
}

bool col::ConfigHandler::SetSize ( wxUint32 varId, const wxSize &rSize )
{
	return SetValue ( varId, boost::str ( boost::format 
		( "%1%,%2%" ) % rSize.GetWidth() % rSize.GetHeight() ) );
}

bool col::ConfigHandler::SetValue ( wxUint32 varId, long value )
{
	return SetValue ( ConvertVarIdToVarName ( varId ), value );
}

bool col::ConfigHandler::SetValue ( wxUint32 varId, const wxString &rValue )
{
	return SetValue ( ConvertVarIdToVarName ( varId ), rValue );
}

const wxString col::ConfigHandler::GetValue ( wxUint32 varId ) const
{
	wxString val;
	GetValue ( ConvertVarIdToVarName ( varId ), val );
	return val;
}

const wxString col::ConfigHandler::GetValue ( const wxString &rVariable ) const
{
	wxString val;
	GetValue ( rVariable, val );
	return val;
}

const col::ConfigVariableDeque &col::ConfigHandler::GetConfigVariables() const 
{ 
	return mCfgVars; 
}

bool col::ConfigHandler::GetValue ( wxUint32 varId, long &rVal ) const
{
	return GetValue ( ConvertVarIdToVarName ( varId ), rVal );
}

bool col::ConfigHandler::SetValueIfMissing ( wxUint32 varId, long value )
{
	const wxString name ( ConvertVarIdToVarName ( varId ) );
	if ( !mpCfg->Exists ( name ) )
	{
		return SetValue ( name, value );
	}
	return false;
}

bool col::ConfigHandler::SetValueIfMissing ( wxUint32 varId, const wxString &rValue )
{
	const wxString name ( ConvertVarIdToVarName ( varId ) );
	if ( !mpCfg->Exists ( name ) )
	{
		return SetValue ( name, rValue );
	}
	return false;
}

bool col::ConfigHandler::SetValue ( const wxString &rVarName, long value ) 
{
	return mpCfg->Write ( rVarName, value );
}

bool col::ConfigHandler::SetValue ( const wxString &rVarName, const wxString &rValue ) 
{
	return mpCfg->Write ( rVarName, rValue );
}

bool col::ConfigHandler::GetValue ( const wxString &rVarName, long &rValue ) const
{
	return mpCfg->Read ( rVarName, &rValue );
}

bool col::ConfigHandler::GetValue ( const wxString &rVarName, wxString &rValue ) const
{
	wxString val;

	if ( mpCfg->Read ( rVarName, &val ) ) 
	{
		rValue = val;
		return true;
	}
	return false;
}

const wxString &col::ConfigHandler::ConvertVarIdToVarName ( wxUint32 varId ) const
{
	ConfigVariableDeque::const_iterator it = std::find ( mCfgVars.begin(), 
		mCfgVars.end(), varId );

	wxASSERT ( it != mCfgVars.end() );
	wxASSERT ( !it->GetName().empty() );

	return it->GetName();
}
